Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(math/curves): implement abstract Curve class and multiple different curve types. #4

Merged
merged 13 commits into from
Sep 30, 2024

Conversation

Baconing
Copy link
Member

@Baconing Baconing commented Sep 30, 2024

This pull requests implements an abstract Curve utility class and implements the following subclasses:

  • ExponentialCurve - y = (x/|x|) * ((1 + exaggeration)^|x| - 1) / exaggeration.
  • LimitedCurve - Limits another curve to a minimum and maximum value.
  • LinearCurve - y = mx + b
  • PiecewiseCurve - Piecewise functions using Java lambdas/functions for conditions
  • QuadraticCurve - Ax^2 + Bx + C
  • RadicalCurve - y = (x+C)^(1/A)+B
  • TimedCurve - An abstract curve that toggles between two outputs based on a timer.
  • NormalTimedCurve - A TimedCurve that toggles between x (when enabled) and curve(x) (when disabled).
  • DoubleTimedCurve - A TimedCurve that toggles between two curves.
  • ZeroTimedCurve - A TimedCurve that toggles between 0 (when disabled) and curve(x) (when enabled).

Also adds the WPILib math library to the project.

Summary by CodeRabbit

  • New Features

    • Integrated WPIMath library for enhanced mathematical computations in robotics.
    • Introduced multiple new curve classes: Curve, DoubleTimedCurve, ExponentialCurve, LimitedCurve, LinearCurve, NormalTimedCurve, PiecewiseCurve, QuadraticCurve, RadicalCurve, TimedCurve, and ZeroTimedCurve to support various mathematical functions and behaviors.
  • Improvements

    • Added functionality for toggling between curves based on conditions and timing, allowing for more dynamic control in robotics applications.

…ent curve types.

This commit adds the following curve types:
 - ExponentialCurve - y = (x/|x|) * ((1 + exaggeration)^|x| - 1) / exaggeration.
 - LimitedCurve - Limits another curve to a minimum and maximum value.
 - LinearCurve - y = mx + b
 - PiecewiseCurve - Piecewise functions using Java lambdas/functions for conditions
 - QuadraticCurve - Ax^2 + Bx + C
 - RadicalCurve - y = (x+C)^(1/A)+B
 - TimedCurve - An abstract curve that toggles between two outputs based on a timer.
 - NormalTimedCurve - A TimedCurve that toggles between x (when enabled) and curve(x) (when disabled).
 - DoubleTimedCurve - A TimedCurve that toggles between two curves.
 - ZeroTimedCurve - A TimedCurve that toggles between 0 (when disabled) and curve(x) (when enabled).
@Baconing Baconing added the Kind/Feature New functionality label Sep 30, 2024
@Baconing Baconing added this to the Version: 2025-1.0.0 milestone Sep 30, 2024
@Baconing Baconing requested a review from Trip-kun September 30, 2024 19:55
@Baconing Baconing self-assigned this Sep 30, 2024
Copy link

coderabbitai bot commented Sep 30, 2024

Warning

Rate limit exceeded

@Baconing has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 14 minutes and 18 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Files that changed from the base of the PR and between d23b088 and 20168b9.

Walkthrough

The pull request introduces several new classes related to mathematical curves in the robotics library, enhancing the functionality for curve manipulation. A new dependency on the WPIMath library is added to the build.gradle file. The changes include the creation of abstract and concrete classes for different types of curves, such as linear, quadratic, exponential, and piecewise curves, each with specific methods for evaluation and manipulation.

Changes

File Path Change Summary
build.gradle Added dependency: compileOnly "edu.wpi.first.wpimath:wpimath-java:2024.3.2"
src/main/java/net/frc5183/librobot/math/curve/*.java Introduced multiple classes (Curve, DoubleTimedCurve, ExponentialCurve, LimitedCurve, LinearCurve, NormalTimedCurve, PiecewiseCurve, QuadraticCurve, RadicalCurve, TimedCurve, ZeroTimedCurve) with methods for mathematical curve evaluation and manipulation.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Curve
    participant TimedCurve
    participant ZeroTimedCurve

    User->>Curve: Create curve instance
    Curve->>User: Return curve object

    User->>TimedCurve: Create timed curve instance
    TimedCurve->>User: Return timed curve object

    User->>ZeroTimedCurve: Create zero timed curve instance
    ZeroTimedCurve->>User: Return zero timed curve object

    User->>TimedCurve: Enable curve
    TimedCurve->>Curve: Evaluate curve
    Curve->>TimedCurve: Return curve value
    TimedCurve->>User: Return enabled curve value

    User->>ZeroTimedCurve: Disable curve
    ZeroTimedCurve->>User: Return zero
Loading

Poem

🐰 In the meadow where curves do play,
New shapes arise, brightening the day.
From linear lines to pieces that bend,
Each curve a story, a twist, a blend.
With math in our hearts, we hop and we cheer,
For curves in our code bring joy, oh dear! 🌼


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

🧹 Outside diff range and nitpick comments (22)
src/main/java/net/frc5183/librobot/math/curve/Curve.java (2)

3-5: Consider enhancing the class-level Javadoc.

While the current Javadoc is clear and concise, it could be more informative. Consider adding details about:

  1. The expected usage of this abstract class.
  2. Any implementation requirements for subclasses.
  3. The significance of this class in the context of the robotics library.

This additional information would provide better guidance for developers working with or extending this class.

Here's a suggested enhancement:

/**
 * An abstract class which represents a mathematical function/curve.
 * <p>
 * This class serves as a base for various curve implementations in the robotics library.
 * Subclasses should implement the {@link #curve(Double)} method to define specific
 * mathematical behaviors.
 * <p>
 * Typical usage involves creating concrete subclasses for different types of curves
 * (e.g., linear, quadratic, exponential) and using them for calculations in robot control systems.
 */

7-11: Consider enhancing the method-level Javadoc.

The current Javadoc for the curve method is good, but it could be improved with some additional details:

  1. Mention any potential exceptions that might be thrown (if applicable).
  2. Provide information about how the method handles edge cases or special values (e.g., null, infinity, NaN).
  3. If there are any constraints on the input or output, they should be documented.

Here's a suggested enhancement:

/**
 * Returns the value of the curve at the given x value.
 *
 * @param x The x value to evaluate the curve at.
 * @return The value of the curve at the given x value.
 * @throws NullPointerException if x is null
 * @implSpec Implementations should handle special cases such as infinity and NaN appropriately.
 * The behavior for these cases should be clearly defined in the implementing class.
 */
src/main/java/net/frc5183/librobot/math/curve/ExponentialCurve.java (3)

3-7: Enhance the class-level Javadoc for clarity.

The Javadoc comment provides a good overview of the class purpose and the mathematical formula. However, it could be improved for better clarity and understanding.

Consider updating the Javadoc as follows:

/**
 * An {@link Curve} implementation representing an exponential function.
 * <p>
 * The curve is defined by the equation:
 * y = (x/|x|) * ((1 + k)^|x| - 1) / k
 * <p>
 * Where:
 * <ul>
 *   <li>x is the input value</li>
 *   <li>k is the exaggeration parameter of the curve</li>
 * </ul>
 * This curve provides a customizable exponential response with the exaggeration parameter.
 */

This enhancement provides a clearer structure and explains the variables in the equation.


13-19: Minor enhancement to constructor Javadoc.

The constructor's Javadoc is good, but we can improve it slightly for better documentation.

Consider updating the Javadoc as follows:

/**
 * Creates a new {@link ExponentialCurve} with the specified exaggeration.
 * 
 * @param exaggeration The exaggeration parameter of the curve. Higher values result in a more pronounced curve.
 */

This change provides more context about the exaggeration parameter's effect on the curve.


21-27: Approve implementation with minor suggestion.

The curve method correctly implements the exponential curve formula and handles the special case for x = 0. The use of Math.abs() and Math.pow() is appropriate.

Consider adding braces to the if statement for consistency and to follow best practices:

@Override
public Double curve(Double x) {
    if (x == 0) {
        return 0d;
    }

    return (x / Math.abs(x)) * ((Math.pow(1 + exaggeration, Math.abs(x))) - 1) / exaggeration;
}

This change addresses the PMD warning about control statement braces and improves code consistency.

🧰 Tools
🪛 GitHub Check: pmd

[warning] 23-23: This statement should have braces
Enforce a policy for braces on control statements. It is recommended to use braces on 'if ... else'
statements and loop statements, even if they are optional. This usually makes the code clearer, and
helps prepare the future when you need to add another statement. That said, this rule lets you control
which statements are required to have braces via properties.

From 6.2.0 on, this rule supersedes WhileLoopMustUseBraces, ForLoopMustUseBraces, IfStmtMustUseBraces,
and IfElseStmtMustUseBraces.

ControlStatementBraces (Priority: 3, Ruleset: Code Style)
https://docs.pmd-code.org/pmd-doc-7.6.0/pmd_rules_java_codestyle.html#controlstatementbraces


[warning] 26-26: Useless parentheses.
Parenthesized expressions are used to override the default operator precedence
rules. Parentheses whose removal would not change the relative nesting of operators
are unnecessary, because they don't change the semantics of the enclosing expression.

Some parentheses that strictly speaking are unnecessary, may still be considered useful
for readability. This rule allows to ignore violations on two kinds of unnecessary parentheses:

  • "Clarifying" parentheses, which separate operators of difference precedence. While
    unnecessary, they make precedence rules explicit, which may be useful for rarely used
    operators. For example:
    (a + b) & c // is equivalent to `a + b & c`, but probably clearer

Unset the property ignoreClarifying to report them.

  • "Balancing" parentheses, which are unnecessary but visually balance out another pair
    of parentheses around an equality operator. For example, those two expressions are equivalent:
    (a == null) != (b == null)
    a == null != (b == null)

The parentheses on the right are required, and the parentheses on the left are
just more visually pleasing. Unset the property ignoreBalancing to report them.

UselessParentheses (Priority: 4, Ruleset: Code Style)
https://docs.pmd-code.org/pmd-doc-7.6.0/pmd_rules_java_codestyle.html#uselessparentheses


[warning] 26-26: Useless parentheses.
Parenthesized expressions are used to override the default operator precedence
rules. Parentheses whose removal would not change the relative nesting of operators
are unnecessary, because they don't change the semantics of the enclosing expression.

Some parentheses that strictly speaking are unnecessary, may still be considered useful
for readability. This rule allows to ignore violations on two kinds of unnecessary parentheses:

  • "Clarifying" parentheses, which separate operators of difference precedence. While
    unnecessary, they make precedence rules explicit, which may be useful for rarely used
    operators. For example:
    (a + b) & c // is equivalent to `a + b & c`, but probably clearer

Unset the property ignoreClarifying to report them.

  • "Balancing" parentheses, which are unnecessary but visually balance out another pair
    of parentheses around an equality operator. For example, those two expressions are equivalent:
    (a == null) != (b == null)
    a == null != (b == null)

The parentheses on the right are required, and the parentheses on the left are
just more visually pleasing. Unset the property ignoreBalancing to report them.

UselessParentheses (Priority: 4, Ruleset: Code Style)
https://docs.pmd-code.org/pmd-doc-7.6.0/pmd_rules_java_codestyle.html#uselessparentheses

src/main/java/net/frc5183/librobot/math/curve/NormalTimedCurve.java (2)

1-6: Enhance class documentation for clarity.

The class documentation provides a concise description of the functionality. However, it could be improved by adding more details about the purpose and usage of the class.

Consider expanding the class documentation to include:

  1. A brief explanation of what a TimedCurve is.
  2. The specific use case or scenario where NormalTimedCurve would be beneficial.
  3. An example of how the class toggles between x and curve(x).

Here's a suggested improvement:

/**
 * A {@link TimedCurve} that toggles between x (when disabled) and curve(x) (when enabled).
 * This class is useful in scenarios where you need to apply a curve transformation
 * to an input value for a specific duration, then revert to the original input.
 * 
 * For example, it can be used in robotics control systems to temporarily modify
 * input values based on certain conditions or time intervals.
 * 
 * When enabled: output = curve(x)
 * When disabled: output = x
 */

23-31: LGTM with a suggestion: Consider adding null check in enabled method.

The implementations of enabled and disabled methods are correct and align with the class description. They are concise and clear in their purpose.

However, to improve robustness, consider adding a null check in the enabled method:

 @Override
 protected Double enabled(Double x) {
+    if (x == null) {
+        return null; // or throw an exception, depending on your error handling strategy
+    }
     return curve(x);
 }

This change will prevent potential NullPointerExceptions if the input x is null.

src/main/java/net/frc5183/librobot/math/curve/LinearCurve.java (1)

33-63: LGTM: Getter and setter methods are well-implemented.

The getter and setter methods for slope and yIntercept are correctly implemented with clear Javadoc comments.

Consider adding validation in the setter methods to ensure that only valid values are set. For example:

public void setSlope(double slope) {
    if (Double.isFinite(slope)) {
        this.slope = slope;
    } else {
        throw new IllegalArgumentException("Slope must be a finite number");
    }
}

This would prevent setting invalid values like NaN or Infinity.

src/main/java/net/frc5183/librobot/math/curve/RadicalCurve.java (3)

3-6: Enhance class-level Javadoc comment

The current Javadoc comment provides a good basic description of the class. However, it could be improved by adding more details about the parameters and their constraints.

Consider expanding the Javadoc comment to include:

  • Descriptions of A, B, and C parameters
  • Any constraints on these parameters (e.g., A != 0)
  • The domain and range of the function

Example:

/**
 * A {@link Curve} which represents a radical equation in the form y = (x+C)^(1/A)+B.
 * 
 * @param A The root degree (A != 0)
 * @param B The vertical shift
 * @param C The horizontal shift
 * 
 * Domain: x >= -C when A > 0, x <= -C when A < 0
 * Range: y >= B when A > 0, y <= B when A < 0
 */

22-32: Enhance constructor Javadoc and consider adding parameter validation

The constructor's Javadoc comment could be more informative about parameter constraints.

  1. Enhance the Javadoc comment to include parameter constraints. For example:
/**
 * Creates a new {@link RadicalCurve} with the given A, B, and C values.
 * @param a The "A" variable in the curve equation. Must not be zero.
 * @param b The "B" variable in the curve equation.
 * @param c The "C" variable in the curve equation.
 * @throws IllegalArgumentException if a is zero.
 */
  1. Consider adding parameter validation in the constructor:
public RadicalCurve(Double a, Double b, Double c) {
    if (a == 0) {
        throw new IllegalArgumentException("Parameter 'a' must not be zero.");
    }
    this.a = a;
    this.b = b;
    this.c = c;
}

40-86: Enhance Javadoc comments for setter methods

The current Javadoc comments for the setter methods could be more informative about the mathematical implications of changing these values.

Consider updating the Javadoc comments for the setter methods to include more details. For example:

/**
 * Sets the "A" variable in the curve equation.
 * Changing this value affects the root degree of the radical function.
 * @param a The new "A" variable in the curve equation. Must not be zero.
 * @throws IllegalArgumentException if a is zero.
 */
public void setA(Double a) {
    if (a == 0) {
        throw new IllegalArgumentException("Parameter 'a' must not be zero.");
    }
    this.a = a;
    this.inverseA = 1 / a; // Update cached value
}

/**
 * Sets the "B" variable in the curve equation.
 * Changing this value shifts the entire curve vertically.
 * @param b The new "B" variable in the curve equation.
 */
public void setB(Double b) {
    this.b = b;
}

/**
 * Sets the "C" variable in the curve equation.
 * Changing this value shifts the curve horizontally and affects the domain of the function.
 * @param c The new "C" variable in the curve equation.
 */
public void setC(Double c) {
    this.c = c;
}

These enhanced comments provide more context about how changing each variable affects the curve's behavior.

src/main/java/net/frc5183/librobot/math/curve/QuadraticCurve.java (2)

1-20: Class structure looks good, consider using primitive types.

The class structure is well-designed for representing a quadratic curve. The fields are appropriately declared as private with clear comments.

Consider using primitive double instead of Double for the coefficients if null values are not needed. This would prevent potential NullPointerExceptions and improve performance. If you decide to keep Double, ensure proper null checks are in place where these fields are used.


48-54: Setter methods are correct, consider adding input validation.

The setter methods for a, b, and c are implemented correctly and have clear, descriptive comments.

Consider adding input validation to prevent setting null values or other invalid states. For example:

public void setA(Double a) {
    if (a == null) {
        throw new IllegalArgumentException("Coefficient 'a' cannot be null");
    }
    this.a = a;
}

Apply similar checks to setB and setC methods. This will help maintain the integrity of the QuadraticCurve object.

Also applies to: 64-70, 80-86

src/main/java/net/frc5183/librobot/math/curve/DoubleTimedCurve.java (2)

3-6: Enhance the class-level Javadoc comment

The current Javadoc provides a basic description of the class. Consider expanding it to include more details about the purpose and behavior of the DoubleTimedCurve class.

Here's a suggested improvement:

/**
 * A {@link TimedCurve} that toggles between two curves based on its enabled/disabled state.
 * This class allows for dynamic switching between two different curve behaviors,
 * defined by the enabledCurve and disabledCurve.
 */
public class DoubleTimedCurve extends TimedCurve {

41-63: Enhance Javadoc for getter methods

While the getter methods are correctly implemented, their Javadoc comments could be more informative. Consider adding more context about when these methods might be used.

Here's a suggested improvement for getEnabledCurve:

/**
 * Returns the curve used when this DoubleTimedCurve is in an enabled state.
 * This curve determines the behavior of the `enabled` method.
 *
 * @return The Curve object used when this DoubleTimedCurve is enabled.
 */
public Curve getEnabledCurve() {
    return enabledCurve;
}

Apply a similar improvement to getDisabledCurve.

src/main/java/net/frc5183/librobot/math/curve/TimedCurve.java (2)

34-37: Consider starting the Timer in the constructor

The constructor initializes the delay fields correctly. However, it doesn't start the Timer. Consider adding timer.start(); at the end of the constructor to ensure the Timer begins running immediately upon object creation.

 public TimedCurve(Double delayEnabled, Double delayDisabled) {
     this.delayEnabled = delayEnabled;
     this.delayDisabled = delayDisabled;
+    timer.start();
 }

39-50: Add a comment explaining the logic and consider using if-else for clarity

The curve method implements the core logic for toggling between enabled and disabled states correctly. However, consider adding a comment explaining the logic and using an if-else statement instead of a ternary operator for better readability.

 @Override
 public Double curve(Double x) {
+    // Check if it's time to toggle the state based on the current state and elapsed time
     if (disabled && timer.hasElapsed(delayDisabled)) {
         disabled = false;
         timer.reset();
     } else if (!disabled && timer.hasElapsed(delayEnabled)) {
         disabled = true;
         timer.reset();
     }

-    return disabled ? disabled(x) : enabled(x);
+    // Return the appropriate value based on the current state
+    if (disabled) {
+        return disabled(x);
+    } else {
+        return enabled(x);
+    }
 }
src/main/java/net/frc5183/librobot/math/curve/PiecewiseCurve.java (5)

19-50: LGTM: Constructors are well-implemented, with a minor suggestion.

The constructors provide good flexibility for creating PiecewiseCurve instances. The use of Map.of() and Map.ofEntries() is appropriate for creating immutable maps.

Consider adding a @SafeVarargs annotation to the constructor that takes a Map<Function<Double, Boolean>, Curve> parameter. While not strictly necessary, it would be consistent with the other varargs constructor and suppress any potential warnings.

+    @SafeVarargs
     public PiecewiseCurve(Map<Function<Double, Boolean>, Curve> curves) {
         this.curves = curves;
     }

52-60: LGTM: curve method implementation is correct, with a potential optimization.

The curve method correctly implements the piecewise function logic, iterating through the conditions and returning the value of the first matching curve. The default return value of 0d is consistent with the class documentation.

Consider using the Stream API for a more concise implementation and potential performance improvement:

     @Override
     public Double curve(Double x) {
-        for (Map.Entry<Function<Double, Boolean>, Curve> entry : curves.entrySet()) {
-            if (entry.getKey().apply(x)) {
-                return entry.getValue().curve(x);
-            }
-        }
-        return 0d;
+        return curves.entrySet().stream()
+            .filter(entry -> entry.getKey().apply(x))
+            .findFirst()
+            .map(entry -> entry.getValue().curve(x))
+            .orElse(0d);
     }

This implementation may be more efficient for large maps, as it can short-circuit the evaluation once a matching condition is found.


86-93: LGTM: replace method is correctly implemented, with a minor suggestion.

The replace method provides a clean way to update a curve for a given condition without exposing the internal map structure.

For consistency with other methods in the class, consider returning the previous curve (if any) associated with the condition:

-    public void replace(Function<Double, Boolean> condition, Curve curve) {
-        curves.replace(condition, curve);
+    public Curve replace(Function<Double, Boolean> condition, Curve curve) {
+        return curves.replace(condition, curve);
     }

This change would make the method more informative and align it with the behavior of java.util.Map.replace().


95-102: LGTM: put method is correctly implemented, with a minor suggestion.

The put method provides a clean way to add a new condition-curve pair to the piecewise curve without exposing the internal map structure.

For consistency with the java.util.Map.put() method and to provide more information to the caller, consider returning the previous curve (if any) associated with the condition:

-    public void put(Function<Double, Boolean> condition, Curve curve) {
-        curves.put(condition, curve);
+    public Curve put(Function<Double, Boolean> condition, Curve curve) {
+        return curves.put(condition, curve);
     }

This change would make the method more informative and align it with the behavior of java.util.Map.put().


1-127: Overall, the PiecewiseCurve class is well-designed and implemented.

The class provides a robust and efficient implementation of a piecewise curve using a map of conditions to curves. The methods offer a good balance of functionality and encapsulation, allowing for easy creation, access, and manipulation of the curve without exposing the internal map structure.

Consider adding a method to combine two PiecewiseCurve instances, which could be useful in more complex scenarios:

public PiecewiseCurve combine(PiecewiseCurve other) {
    Map<Function<Double, Boolean>, Curve> combinedCurves = new HashMap<>(this.curves);
    other.forEachEntry((condition, curve) -> combinedCurves.putIfAbsent(condition, curve));
    return new PiecewiseCurve(combinedCurves);
}

This method would allow users to create more complex piecewise curves by combining existing ones, enhancing the flexibility and composability of the class.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 9c53720 and feed830.

📒 Files selected for processing (12)
  • build.gradle (1 hunks)
  • src/main/java/net/frc5183/librobot/math/curve/Curve.java (1 hunks)
  • src/main/java/net/frc5183/librobot/math/curve/DoubleTimedCurve.java (1 hunks)
  • src/main/java/net/frc5183/librobot/math/curve/ExponentialCurve.java (1 hunks)
  • src/main/java/net/frc5183/librobot/math/curve/LimitedCurve.java (1 hunks)
  • src/main/java/net/frc5183/librobot/math/curve/LinearCurve.java (1 hunks)
  • src/main/java/net/frc5183/librobot/math/curve/NormalTimedCurve.java (1 hunks)
  • src/main/java/net/frc5183/librobot/math/curve/PiecewiseCurve.java (1 hunks)
  • src/main/java/net/frc5183/librobot/math/curve/QuadraticCurve.java (1 hunks)
  • src/main/java/net/frc5183/librobot/math/curve/RadicalCurve.java (1 hunks)
  • src/main/java/net/frc5183/librobot/math/curve/TimedCurve.java (1 hunks)
  • src/main/java/net/frc5183/librobot/math/curve/ZeroTimedCurve.java (1 hunks)
🧰 Additional context used
🪛 GitHub Check: pmd
src/main/java/net/frc5183/librobot/math/curve/ExponentialCurve.java

[warning] 23-23: This statement should have braces
Enforce a policy for braces on control statements. It is recommended to use braces on 'if ... else'
statements and loop statements, even if they are optional. This usually makes the code clearer, and
helps prepare the future when you need to add another statement. That said, this rule lets you control
which statements are required to have braces via properties.

From 6.2.0 on, this rule supersedes WhileLoopMustUseBraces, ForLoopMustUseBraces, IfStmtMustUseBraces,
and IfElseStmtMustUseBraces.

ControlStatementBraces (Priority: 3, Ruleset: Code Style)
https://docs.pmd-code.org/pmd-doc-7.6.0/pmd_rules_java_codestyle.html#controlstatementbraces


[warning] 26-26: Useless parentheses.
Parenthesized expressions are used to override the default operator precedence
rules. Parentheses whose removal would not change the relative nesting of operators
are unnecessary, because they don't change the semantics of the enclosing expression.

Some parentheses that strictly speaking are unnecessary, may still be considered useful
for readability. This rule allows to ignore violations on two kinds of unnecessary parentheses:

  • "Clarifying" parentheses, which separate operators of difference precedence. While
    unnecessary, they make precedence rules explicit, which may be useful for rarely used
    operators. For example:
    (a + b) & c // is equivalent to `a + b & c`, but probably clearer

Unset the property ignoreClarifying to report them.

  • "Balancing" parentheses, which are unnecessary but visually balance out another pair
    of parentheses around an equality operator. For example, those two expressions are equivalent:
    (a == null) != (b == null)
    a == null != (b == null)

The parentheses on the right are required, and the parentheses on the left are
just more visually pleasing. Unset the property ignoreBalancing to report them.

UselessParentheses (Priority: 4, Ruleset: Code Style)
https://docs.pmd-code.org/pmd-doc-7.6.0/pmd_rules_java_codestyle.html#uselessparentheses


[warning] 26-26: Useless parentheses.
Parenthesized expressions are used to override the default operator precedence
rules. Parentheses whose removal would not change the relative nesting of operators
are unnecessary, because they don't change the semantics of the enclosing expression.

Some parentheses that strictly speaking are unnecessary, may still be considered useful
for readability. This rule allows to ignore violations on two kinds of unnecessary parentheses:

  • "Clarifying" parentheses, which separate operators of difference precedence. While
    unnecessary, they make precedence rules explicit, which may be useful for rarely used
    operators. For example:
    (a + b) & c // is equivalent to `a + b & c`, but probably clearer

Unset the property ignoreClarifying to report them.

  • "Balancing" parentheses, which are unnecessary but visually balance out another pair
    of parentheses around an equality operator. For example, those two expressions are equivalent:
    (a == null) != (b == null)
    a == null != (b == null)

The parentheses on the right are required, and the parentheses on the left are
just more visually pleasing. Unset the property ignoreBalancing to report them.

UselessParentheses (Priority: 4, Ruleset: Code Style)
https://docs.pmd-code.org/pmd-doc-7.6.0/pmd_rules_java_codestyle.html#uselessparentheses

🔇 Additional comments (34)
src/main/java/net/frc5183/librobot/math/curve/Curve.java (3)

1-1: LGTM: Package declaration is correct and follows conventions.

The package name net.frc5183.librobot.math.curve is well-structured and follows Java naming conventions. It clearly indicates that this is part of a robotics library (librobot) and specifically belongs to the math and curve-related functionality.


6-6: LGTM: Class declaration is correct and follows best practices.

The Curve class is properly declared as an abstract public class. This aligns with its intended purpose as a base class for various curve implementations. The class name is concise, meaningful, and follows Java naming conventions.


12-12: Consider using primitive double instead of Double.

The curve method is correctly declared as abstract and public. However, the use of Double for both parameter and return type instead of the primitive double is worth discussing:

  1. Using Double allows for null values, which might be intentional for representing undefined points on the curve.
  2. However, it also introduces the possibility of null pointer exceptions if not handled carefully.
  3. Using primitive double would be more efficient and safer if null values are not needed.

Consider whether allowing null values is necessary for your use case. If not, using double might be preferable:

public abstract double curve(double x);

If you decide to keep Double, ensure that null checks are implemented in all concrete subclasses to prevent null pointer exceptions.

To verify the current usage, you can run the following command:

This will help identify existing implementations and how they handle potential null values.

src/main/java/net/frc5183/librobot/math/curve/ExponentialCurve.java (1)

1-44: Overall assessment: Well-implemented with minor suggestions for improvement.

The ExponentialCurve class is a solid implementation of the exponential curve as described in the PR objectives. It correctly extends the Curve abstract class and implements the mathematical formula as specified. The code is generally well-structured and documented.

Key points:

  1. The class-level Javadoc could be enhanced for better clarity.
  2. The curve method correctly implements the formula and handles edge cases.
  3. Consider adding input validation to the setExaggeration method.
  4. Minor improvements to code style (adding braces to if statement) have been suggested.

These minor enhancements will further improve the already well-implemented class.

🧰 Tools
🪛 GitHub Check: pmd

[warning] 23-23: This statement should have braces
Enforce a policy for braces on control statements. It is recommended to use braces on 'if ... else'
statements and loop statements, even if they are optional. This usually makes the code clearer, and
helps prepare the future when you need to add another statement. That said, this rule lets you control
which statements are required to have braces via properties.

From 6.2.0 on, this rule supersedes WhileLoopMustUseBraces, ForLoopMustUseBraces, IfStmtMustUseBraces,
and IfElseStmtMustUseBraces.

ControlStatementBraces (Priority: 3, Ruleset: Code Style)
https://docs.pmd-code.org/pmd-doc-7.6.0/pmd_rules_java_codestyle.html#controlstatementbraces


[warning] 26-26: Useless parentheses.
Parenthesized expressions are used to override the default operator precedence
rules. Parentheses whose removal would not change the relative nesting of operators
are unnecessary, because they don't change the semantics of the enclosing expression.

Some parentheses that strictly speaking are unnecessary, may still be considered useful
for readability. This rule allows to ignore violations on two kinds of unnecessary parentheses:

  • "Clarifying" parentheses, which separate operators of difference precedence. While
    unnecessary, they make precedence rules explicit, which may be useful for rarely used
    operators. For example:
    (a + b) & c // is equivalent to `a + b & c`, but probably clearer

Unset the property ignoreClarifying to report them.

  • "Balancing" parentheses, which are unnecessary but visually balance out another pair
    of parentheses around an equality operator. For example, those two expressions are equivalent:
    (a == null) != (b == null)
    a == null != (b == null)

The parentheses on the right are required, and the parentheses on the left are
just more visually pleasing. Unset the property ignoreBalancing to report them.

UselessParentheses (Priority: 4, Ruleset: Code Style)
https://docs.pmd-code.org/pmd-doc-7.6.0/pmd_rules_java_codestyle.html#uselessparentheses


[warning] 26-26: Useless parentheses.
Parenthesized expressions are used to override the default operator precedence
rules. Parentheses whose removal would not change the relative nesting of operators
are unnecessary, because they don't change the semantics of the enclosing expression.

Some parentheses that strictly speaking are unnecessary, may still be considered useful
for readability. This rule allows to ignore violations on two kinds of unnecessary parentheses:

  • "Clarifying" parentheses, which separate operators of difference precedence. While
    unnecessary, they make precedence rules explicit, which may be useful for rarely used
    operators. For example:
    (a + b) & c // is equivalent to `a + b & c`, but probably clearer

Unset the property ignoreClarifying to report them.

  • "Balancing" parentheses, which are unnecessary but visually balance out another pair
    of parentheses around an equality operator. For example, those two expressions are equivalent:
    (a == null) != (b == null)
    a == null != (b == null)

The parentheses on the right are required, and the parentheses on the left are
just more visually pleasing. Unset the property ignoreBalancing to report them.

UselessParentheses (Priority: 4, Ruleset: Code Style)
https://docs.pmd-code.org/pmd-doc-7.6.0/pmd_rules_java_codestyle.html#uselessparentheses

src/main/java/net/frc5183/librobot/math/curve/NormalTimedCurve.java (3)

6-10: LGTM: Class declaration and member variable are well-defined.

The class extension and member variable declaration are correct. The documentation for the curve variable is clear and concise.


12-21: LGTM: Constructor is well-implemented and documented.

The constructor is correctly implemented, calling the superclass constructor and initializing the curve member variable. The documentation clearly describes all parameters.


33-47: LGTM: Getter and setter methods are well-implemented and documented.

The getter and setter methods for the curve variable are correctly implemented and follow Java conventions. The documentation for both methods is clear and concise.

src/main/java/net/frc5183/librobot/math/curve/ZeroTimedCurve.java (7)

1-6: LGTM: Class declaration and package statement are well-structured.

The class declaration, package statement, and Javadoc comment are clear and follow Java conventions. The class name ZeroTimedCurve is descriptive and accurately represents its functionality.


7-10: LGTM: Field declaration is properly encapsulated and documented.

The curve field is correctly declared as private with an informative Javadoc comment, adhering to good Java practices.


12-21: LGTM: Constructor is well-implemented and documented.

The constructor is properly implemented, calling the superclass constructor and initializing the curve field. The Javadoc comment clearly explains all parameters.


23-26: LGTM: enabled method correctly implements the specified behavior.

The enabled method properly overrides the superclass method and correctly returns the value of the curve when enabled.


28-31: LGTM: disabled method correctly implements the specified behavior.

The disabled method properly overrides the superclass method and correctly returns 0.0 when the curve is disabled.


33-47: LGTM: Getter and setter methods are properly implemented and documented.

The getCurve() and setCurve(Curve curve) methods are correctly implemented with appropriate Javadoc comments.


1-48: Excellent implementation of the ZeroTimedCurve class.

The ZeroTimedCurve class is well-implemented, following Java best practices and conventions. It correctly extends TimedCurve and implements the behavior described in the PR objectives. The code is clean, well-documented, and properly encapsulated. Great job!

src/main/java/net/frc5183/librobot/math/curve/LinearCurve.java (4)

1-6: LGTM: Class declaration and package statement are well-structured.

The class declaration, package statement, and class-level Javadoc comment are appropriately implemented. The class extends Curve as expected for a specific curve type.


7-15: LGTM: Field declarations are well-implemented.

The private fields slope and yIntercept are appropriately declared with clear Javadoc comments. The use of double type is suitable for these mathematical concepts.


17-25: LGTM: Constructor is well-implemented.

The constructor correctly initializes the slope and yIntercept fields with the provided values. The Javadoc comment clearly describes the purpose and parameters of the constructor.


1-64: Overall, LinearCurve is well-implemented with room for minor enhancements.

The LinearCurve class provides a solid implementation of a linear curve. It correctly extends the Curve class, provides appropriate fields with getters and setters, and implements the curve method as expected. The code is well-documented with clear Javadoc comments.

Consider the suggested improvements regarding primitive types, null checks, and input validation to further enhance the robustness of the class. These changes would make the class more resilient to edge cases and potential misuse.

Great job on implementing this mathematical concept in a clear and concise manner!

build.gradle (1)

47-47: LGTM: WPIMath dependency added correctly

The addition of the WPIMath library dependency is correct and consistent with other WPILib dependencies. This change aligns well with the PR objectives, which mention integrating the WPILib math library into the project.

src/main/java/net/frc5183/librobot/math/curve/LimitedCurve.java (2)

1-6: LGTM: Class declaration and documentation are well-defined.

The class declaration and its accompanying Javadoc comment are clear and concise, effectively communicating the purpose of the LimitedCurve class.


1-87: Overall assessment: Well-structured class with room for robustness improvements.

The LimitedCurve class is well-designed and implements the intended functionality effectively. However, there are several opportunities to enhance its robustness and type safety:

  1. Consider using primitive double instead of Double for min and max to prevent null values and potential null pointer exceptions.
  2. Add parameter validation in the constructor to ensure valid input.
  3. Implement null checks in the curve method to handle cases where limits are not set.
  4. Add input validation in setter methods to maintain consistent state.

Addressing these points will significantly improve the reliability and usability of the LimitedCurve class.

src/main/java/net/frc5183/librobot/math/curve/RadicalCurve.java (1)

1-87: Overall assessment of RadicalCurve class

The RadicalCurve class is a well-structured implementation of a radical curve. It correctly extends the Curve class and provides the necessary functionality to represent and manipulate a radical equation.

Positive aspects:

  1. Clear class structure with appropriate member variables.
  2. Correct implementation of the radical equation in the curve method.
  3. Provision of getter and setter methods for all parameters.

Areas for improvement:

  1. Enhanced exception handling in the curve method and constructor.
  2. More detailed Javadoc comments, especially for the class and setter methods.
  3. Consideration of using primitive types instead of wrapper classes for member variables.
  4. Potential performance optimization by caching the inverse of 'a'.

Implementing these suggestions will result in a more robust, efficient, and well-documented class.

src/main/java/net/frc5183/librobot/math/curve/QuadraticCurve.java (3)

22-32: Constructor implementation is correct and well-documented.

The constructor correctly initializes the fields with the provided values. The comment clearly explains the purpose and parameters of the constructor.


40-46: Getter methods are correctly implemented and well-documented.

The getter methods for a, b, and c are implemented correctly. Each method has a clear and descriptive comment explaining its purpose.

Also applies to: 56-62, 72-78


1-87: Overall, the QuadraticCurve implementation is well-structured and documented.

The class provides a solid representation of a quadratic curve with appropriate fields, constructor, and methods. The code is generally well-written and commented. The suggested improvements (performance optimization in the curve method, null checks, and input validation in setters) will enhance the robustness and efficiency of the implementation without changing its core functionality.

src/main/java/net/frc5183/librobot/math/curve/DoubleTimedCurve.java (4)

7-15: Field declarations look good

The private fields enabledCurve and disabledCurve are well-declared with appropriate Javadoc comments. They follow good encapsulation practices and naming conventions.


17-29: Constructor implementation is correct and well-documented

The constructor is properly implemented, initializing all necessary fields and calling the superclass constructor. The Javadoc comment is comprehensive, including descriptions for all parameters and a reference to the superclass constructor.


31-39: Overridden methods are correctly implemented

The enabled and disabled methods are properly overridden and implement the expected behavior of delegating to their respective curves. The use of the @OverRide annotation is correct.


1-72: Overall, well-implemented class with minor improvement opportunities

The DoubleTimedCurve class is well-structured and correctly implements the intended functionality. It adheres to Java conventions and OOP principles. The suggested improvements, including enhanced Javadoc comments, input validation for setters, and the addition of a toString() method, will further increase the robustness and maintainability of the class.

src/main/java/net/frc5183/librobot/math/curve/TimedCurve.java (2)

1-27: LGTM: Well-structured class declaration and fields

The abstract class declaration and field definitions are well-structured. The use of Double for delay values allows for fractional seconds, providing good precision for timing control.


52-64: LGTM: Well-defined abstract methods

The abstract methods enabled(Double x) and disabled(Double x) are well-defined with clear and informative JavaDoc comments. This design allows for flexible implementations in subclasses.

src/main/java/net/frc5183/librobot/math/curve/PiecewiseCurve.java (3)

1-17: LGTM: Class declaration and field look good.

The PiecewiseCurve class is correctly declared as extending Curve, and the curves field is appropriately defined as a private final Map. This design choice allows for efficient storage and retrieval of condition-curve pairs.


62-84: LGTM: forEach methods are well-implemented.

The forEach, forEachKey, and forEachEntry methods provide a convenient and type-safe way to iterate over the curves, conditions, and both respectively. The use of Consumer and BiConsumer is appropriate, and the implementation correctly delegates to the underlying map's methods.


104-126: LGTM: get, remove, and clear methods are correctly implemented.

The get, remove, and clear methods provide essential operations for manipulating the piecewise curve without exposing the internal map structure. The implementations are straightforward and correctly delegate to the corresponding methods of the underlying map.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (1)
src/main/java/net/frc5183/librobot/math/curve/ExponentialCurve.java (1)

21-27: Consider adding Javadoc to the curve method

The curve method is well-implemented and correctly handles the case where x is 0. However, it would benefit from additional documentation to explain its behavior and parameters.

Consider adding a Javadoc comment to the curve method:

/**
 * Calculates the y-value of the exponential curve for a given x-value.
 * @param x The input value.
 * @return The calculated y-value of the curve.
 * @implNote Returns 0 when x is 0 to avoid division by zero.
 */
@Override
public Double curve(Double x) {
    // ... existing implementation ...
}

This additional documentation will improve the readability and maintainability of the code.

🧰 Tools
🪛 GitHub Check: pmd

[warning] 23-23: This statement should have braces
Enforce a policy for braces on control statements. It is recommended to use braces on 'if ... else'
statements and loop statements, even if they are optional. This usually makes the code clearer, and
helps prepare the future when you need to add another statement. That said, this rule lets you control
which statements are required to have braces via properties.

From 6.2.0 on, this rule supersedes WhileLoopMustUseBraces, ForLoopMustUseBraces, IfStmtMustUseBraces,
and IfElseStmtMustUseBraces.

ControlStatementBraces (Priority: 3, Ruleset: Code Style)
https://docs.pmd-code.org/pmd-doc-7.6.0/pmd_rules_java_codestyle.html#controlstatementbraces

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between feed830 and d23b088.

📒 Files selected for processing (1)
  • src/main/java/net/frc5183/librobot/math/curve/ExponentialCurve.java (1 hunks)
🧰 Additional context used
🪛 GitHub Check: pmd
src/main/java/net/frc5183/librobot/math/curve/ExponentialCurve.java

[warning] 23-23: This statement should have braces
Enforce a policy for braces on control statements. It is recommended to use braces on 'if ... else'
statements and loop statements, even if they are optional. This usually makes the code clearer, and
helps prepare the future when you need to add another statement. That said, this rule lets you control
which statements are required to have braces via properties.

From 6.2.0 on, this rule supersedes WhileLoopMustUseBraces, ForLoopMustUseBraces, IfStmtMustUseBraces,
and IfElseStmtMustUseBraces.

ControlStatementBraces (Priority: 3, Ruleset: Code Style)
https://docs.pmd-code.org/pmd-doc-7.6.0/pmd_rules_java_codestyle.html#controlstatementbraces

🔇 Additional comments (3)
src/main/java/net/frc5183/librobot/math/curve/ExponentialCurve.java (3)

1-11: LGTM: Well-structured class with clear documentation

The class declaration, documentation, and field definition are well-implemented. The Javadoc comment clearly explains the purpose of the class and the mathematical formula it represents, which is helpful for understanding and maintenance.


29-43: Implement input validation in setExaggeration method

The getter method is well-implemented and documented. However, as noted in a previous review comment, the setExaggeration method should include input validation to ensure the exaggeration value remains positive.

I agree with the previous review comment. Please implement the suggested input validation in the setExaggeration method to maintain the integrity of the exponential curve calculation.


23-23: Consider the trade-off between conciseness and consistency for braces usage

A static analysis tool has suggested adding braces to the if statement on this line. While using braces consistently can improve readability and reduce the risk of errors when modifying code in the future, the current implementation is clear and concise for a single-line return statement.

Consider the coding style guidelines for your project. If there's no strict rule requiring braces for all control statements, the current implementation can be maintained for its simplicity. However, if consistency is a priority, you may want to add braces:

if (x == 0) {
    return 0d;
}

Please verify the preferred style for your project and update if necessary.

🧰 Tools
🪛 GitHub Check: pmd

[warning] 23-23: This statement should have braces
Enforce a policy for braces on control statements. It is recommended to use braces on 'if ... else'
statements and loop statements, even if they are optional. This usually makes the code clearer, and
helps prepare the future when you need to add another statement. That said, this rule lets you control
which statements are required to have braces via properties.

From 6.2.0 on, this rule supersedes WhileLoopMustUseBraces, ForLoopMustUseBraces, IfStmtMustUseBraces,
and IfElseStmtMustUseBraces.

ControlStatementBraces (Priority: 3, Ruleset: Code Style)
https://docs.pmd-code.org/pmd-doc-7.6.0/pmd_rules_java_codestyle.html#controlstatementbraces

build.gradle Outdated Show resolved Hide resolved
Trip-kun
Trip-kun previously approved these changes Sep 30, 2024
Copy link

@Trip-kun Trip-kun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. Look over Radical curve as discussed in the other comment and then you're good to go.

@Baconing Baconing merged commit 1e0ce6d into master Sep 30, 2024
1 of 2 checks passed
@Baconing Baconing deleted the v2025-1-0-0/math branch October 10, 2024 17:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Kind/Feature New functionality
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants